﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Web;
using System.Web.Mvc;

namespace CollectionBindingExample
{
	public class ArrayBinder : DefaultModelBinder
	{
		public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
		{
			var model = base.BindModel(controllerContext, bindingContext);
	
			if (model == null)
			{
				Type modelType = bindingContext.ModelType;
				model = CreateEnumerableIfAppropriate(modelType);
			}

			return model;
		}

		protected override void BindProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor)
		{
			base.BindProperty(controllerContext, bindingContext, propertyDescriptor);

			var value = propertyDescriptor.GetValue(bindingContext.Model);

			if (value != null) return;

			var type = propertyDescriptor.PropertyType;
			value = this.CreateEnumerableIfAppropriate(type);

			if (value != null)
			{
				propertyDescriptor.SetValue(bindingContext.Model, value);
			}
		}


		private readonly IEnumerable<Type> genericEnumerableTypes = new Type[] { typeof(IEnumerable<>), typeof(ICollection<>), typeof(IList<>) };
		private object CreateEnumerableIfAppropriate(Type type)
		{
			Type typeToCreate = null;


			if (type.IsArray)
			{
				return Array.CreateInstance(type.GetElementType(), 0);
			}

			Type genericTypeDefinition = type.GetGenericTypeDefinition();
			
			if (genericTypeDefinition == typeof(IDictionary<,>))
			{
				typeToCreate = typeof(Dictionary<,>).MakeGenericType(type.GetGenericArguments());
			}
			else if (this.genericEnumerableTypes.Contains(genericTypeDefinition))
			{
				typeToCreate = typeof(List<>).MakeGenericType(type.GetGenericArguments());
			}
			else
			{
				return null;
			}

			return Activator.CreateInstance(typeToCreate);
		}
	}
}